import json

from bson.objectid import ObjectId

from api_base import *
from docker_client import stop_and_remove_container_by_id


class APIMyClassHandler(APIBaseHandler):
    @check_role(RoleStudent)
    async def get(self):
        '''
        获取我加入的课堂
        '''
        skip = 0
        limit = 0
        try:
            skip = int(self.get_argument('skip'))
            limit = int(self.get_argument('limit'))
        except:
            pass
        classrooms = []
        async for item in self.db.classrooms.find({'member': {'$all': [self.current_user]}}).limit(limit).skip(skip):
            classrooms.append({
                'classid': str(item['_id']),
                'classname': item['classname']
            })
        self.write_json(classrooms)


class APIClassHandler(APIBaseHandler):
    @check_role(RoleStudent)
    async def get(self):
        '''
        课堂列表
        '''
        skip = 0
        limit = 0
        try:
            skip = int(self.get_argument('skip'))
            limit = int(self.get_argument('limit'))
        except:
            pass
        classrooms = []
        async for item in self.db.classrooms.find({'public': '1'}).limit(limit).skip(skip):
            classrooms.append({
                'classid': str(item['_id']),
                'classname': item['classname']
            })
        self.write_json(classrooms)

    @check_role(RoleStudent)
    async def post(self):
        '''
        查询课堂
        '''
        try:
            js = json.loads(self.request.body)
            classidstr = js['classid']
        except:
            self.write_json({'info': 'json_error'})
            return
        detail = await self.db.classrooms.find_one({'_id': ObjectId(classidstr)})
        if not detail:
            self.write_json({'info': 'unknown class'})
            return
        self.write_json({'info': 'ok', 'classname': detail['classname']})


class APIClassDetailHandler(APIBaseHandler):
    @check_role(RoleStudent)
    async def get(self):
        '''
        获取课堂信息
        '''
        classidstr = get_id(self.request.uri)
        classid = ObjectId(classidstr)
        detail = await self.db.classrooms.find_one({'_id': classid})
        if not detail:
            self.write_json({'info': 'unknown class'})
            return
        detail.pop('_id')
        self.write_json(detail)

    @check_role(RoleStudent)
    async def post(self):
        '''
        加入或退出课堂
        '''
        classidstr = get_id(self.request.uri)
        classid = ObjectId(classidstr)
        try:
            js = json.loads(self.request.body)
            op = js['op']
        except:
            self.write_json({'info': 'json_error'})
            return
        if op == 'join':
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$addToSet': {'member': self.current_user}})
        elif op == 'exit':
            res = await self.db.classrooms.find_one({'_id': classid})
            if not res:
                self.write_json({'info': 'unknown class'})
                return
            labids = [x['labid'] for x in res['labs']]
            # WARNING
            async for item in self.db.containers.find({'username': self.current_user, 'labid': {'$in': labids}}):
                await stop_and_remove_container_by_id(item['containerid'])
            await self.db.containers.delete_many({'username': self.current_user, 'labid': {'$in': labids}})
            await self.db.classrooms.update_one(
                {'_id': classid}, {'$pull': {'teacher': self.current_user}})
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$pull': {'member': self.current_user}})
        else:
            self.write_json({'info': 'unknown op'})
            return
        self.write_json({'info': 'ok',
                         'matched': res.matched_count,
                         'modified': res.modified_count})


class APIClassAdminHandler(APIBaseHandler):
    MUST_FIELDS = ['title', 'image']
    OPTIONAL_FIELDS = [('desc', ''), ('intro', ''),
                       ('steps', []), ('finish', ''), ('files', [])]

    @check_role(RoleTeacher)
    async def get(self):
        '''
        创建课堂
        '''
        try:
            classname = self.get_argument('classname')
            public = self.get_argument('public')
        except:
            self.write_json({'info': 'arg error'})
            return
        res = await self.db.classrooms.insert_one({
            'classname': classname,
            'teacher': [self.current_user],
            'member': [self.current_user],
            'labs': [],
            'public': public
        })
        self.write_json({'info': 'ok', 'classid': str(res.inserted_id)})

    @check_role(RoleTeacher)
    async def post(self):
        '''
        管理课堂
        '''
        try:
            js = json.loads(self.request.body)
            op = js['op']
            classidstr = js['classid']
            classid = ObjectId(classidstr)
            objs = js['objs']
            if op == 'add_lab':
                lab_setting = {k: objs[k] for k in self.MUST_FIELDS}
                lab_setting.update(
                    {k: objs.get(k) or d for k, d in self.OPTIONAL_FIELDS})
                lab_setting.update({'inclass': classidstr})
        except:
            self.write_json({'info': 'json_error'})
            return
        if self.role < RoleAdmin:
            res = await self.db.classrooms.find_one(
                {'_id': classid, 'teacher': {'$all': [self.current_user]}})
            if not res:
                self.write_json({'info': 'no access'})
                return
        # TODO filter objs
        if op == 'set_teacher':
            await self.db.classrooms.update_one(
                {'_id': classid}, {'$addToSet': {'member': {'$each': objs}}})
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$addToSet': {'teacher': {'$each': objs}}})
        elif op == 'unset_teacher':
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$pullAll': {'teacher': objs}})
        elif op == 'add_student':
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$addToSet': {'member': {'$each': objs}}})
        elif op == 'del_student':
            res = await self.db.classrooms.find_one({'_id': classid})
            if not res:
                self.write_json({'info': 'unknown class'})
                return
            labids = [x['labid'] for x in res['labs']]
            # WARNING
            async for item in self.db.containers.find({'username': {'$in': objs}, 'labid': {'$in': labids}}):
                await stop_and_remove_container_by_id(item['containerid'])
            await self.db.containers.delete_many({'username': {'$in': objs}, 'labid': {'$in': labids}})
            await self.db.classrooms.update_one(
                {'_id': classid}, {'$pullAll': {'teacher': objs}})
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$pullAll': {'member': objs}})
        elif op == 'add_lab':
            res = await self.db.images.find_one({'image': lab_setting['image']})
            if not res:
                self.write_json({'info': 'unknown image'})
                return
            res = await self.db.labs.insert_one(lab_setting)
            to_add = {
                'title': lab_setting['title'],
                'labid': str(res.inserted_id),
            }
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$addToSet': {'labs': to_add}})
        elif op == 'del_lab':
            res = await self.db.labs.delete_one({'_id': {'$in': [ObjectId(x) for x in objs]}})
            # WARNING
            async for item in self.db.containers.find({'labid': {'$in': objs}}):
                await stop_and_remove_container_by_id(item['containerid'])
            res = await self.db.containers.delete_many({'labid': {'$in': objs}})
            res = await self.db.classrooms.update_one(
                {'_id': classid}, {'$pull': {'labs': {'labid': {'$in': objs}}}})
        else:
            self.write_json({'info': 'unknown op'})
            return
        self.write_json({'info': 'ok',
                         'matched': res.matched_count,
                         'modified': res.modified_count})
